const fs = require('fs');
const path = require('path');
// var async = require('async');
const { Client } = require('ssh2');
const { Print } = require('../print/print');
const { Myfs } = require('../fs/fs');


class MySSH {

    constructor(config) {
        this.config = config;
    }

    connect = (callBack) => {
        const conn = new Client();
        conn.on('ready', () => {
            callBack(conn);
        }).on('error', (err) => {
            console.log(err);
            Print.error(`服务器链接出错！`);
        }).on('end', () => {
        }).on('close', () => {
        }).connect(this.config);
    }

    /**
     * 运行命令
     * @param {string} cmd 
     * @returns {Promise<any>}
     */
    exec = (cmd) => {
        return new Promise((resolve) => {
            this.connect(conn => {
                conn.exec(cmd, (err, stream) => {
                    if (err) {
                        Print.error(`命令运行报错: ${cmd}`);
                        console.log(err);
                        resolve(false);
                    } else {
                        stream.on('close', () => {
                            conn.end();
                            resolve(undefined);
                        }).on('data', (data) => {
                            console.log(data + '');
                        });
                    }
                })
            })
        });
    }

    /**
     * 运行命令数组
     * @param {string[]} cmds 
     * @returns {Promise<any>}
     */
    shell = (cmds) => {
        let command = ``;
        cmds.forEach(v => {
            command += `${v}\n`
        });
        command += `exit\n`;
        return new Promise((resolve) => {
            this.connect(conn => {
                conn.shell((err, stream) => {
                    if (err) {
                        Print.error(`shell命令运行报错: ${command}`);
                        console.log(err);
                        resolve(false);
                    } else {
                        stream.on('close', () => {
                            conn.end();
                            resolve(true);
                        }).on('data', (data) => {
                            console.log(data + '');
                        });
                        stream.end(`${command}`);
                    }

                })
            })
        });
    }

    /**
     * 上传文件
     * @param {string} localFilePath 本地文件地址 './zip/abc.zip'
     * @param {string} remoteFilePath 服务器文件地址 '/var/www/Cloud_CFS/Assets/test/test.zip'
     * @returns {Promise<boolean>}
     */
    uploadFile = (localFilePath, remoteFilePath) => {
        return new Promise((resolve) => {
            this.connect((conn) => {
                conn.sftp((err, sftp) => {
                    if (err) {
                        Print.error(`sftp上传文件报错`);
                        console.log(err);
                        resolve(false);
                    }
                    sftp.fastPut(localFilePath, remoteFilePath, (err) => {
                        if (err) {
                            Print.error(`sftp上传文件报错`);
                            console.log(err);
                            resolve(false);
                        }
                        conn.end();
                        resolve(true);
                    });
                });
            });
        });
    }


    /**
     * 上传文件夹
     * @param {string} localPath 
     * @param {string} remotePath 
     * @returns {Promise<any>}
     */
    uploadDir = async (localPath, remotePath) => {
        const localPathName = path.join(localPath);
        const { dirs, files } = Myfs.getFileAndDirList(localPath);
        // console.log({ dirs, files });

        // 创建远程目录
        const creatRemoteDirCmds = [];
        for (let i = 0; i < dirs.length; i++) {
            const dir = dirs[i];
            const to = `${remotePath}/${dir.slice(localPathName.length + 1)}`;
            const cmd = `mkdir -p ${to}`;
            creatRemoteDirCmds.push(cmd);
        }
        await this.shell(creatRemoteDirCmds);

        // 上传文件
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            const remoteFilePath = `${remotePath}/${file.slice(localPathName.length + 1)}`;
            await this.uploadFile(file, remoteFilePath);
        }

        return Promise.resolve();
    }

    /**
     * 文件下载
     * @param {string} remoteFilePath 服务器文件地址
     * @param {string} localFilePath 本地文件地址
     * @returns {Promise<boolean>}
     */
    downloadFile = (remoteFilePath, localFilePath) => {
        return new Promise((resolve) => {
            this.connect((conn) => {
                conn.sftp((err, sftp) => {
                    if (err) {
                        Print.error(`sftp下载文件报错`);
                        console.log(err);
                        resolve(false);
                    }
                    sftp.fastGet(remoteFilePath, localFilePath, (err) => {
                        if (err) {
                            Print.error(`sftp下载文件报错`);
                            console.log(err);
                            resolve(false);
                        }
                        conn.end();
                        // Print.success(`下载文件 ${localFilePath} 完成`);
                        resolve(true);
                    });
                });
            });
        });
    }

    /**
     * 下载文件夹
     * @param {string} remotePath 
     * @param {string} localPath 
     * @returns {Promise<any>}
     */
    downloadDir = async (remotePath, localPath) => {
        Print.indigo(`扫描远程文件...`);
        const { dirs, files, size, filesNumber, dirsNumber } = await this.scanDir(remotePath);
        Print.success(`扫描结果：${filesNumber}个文件，${dirsNumber}个文件夹，大小：${(size / 1024 / 1024).toFixed(2)}M`);

        // 复制远程目录到本地
        const localDirs = [];
        dirs.forEach(dir => {
            localDirs.push(path.join(localPath, dir.relativePath));
        });
        Myfs.autoCreatDirs(localDirs);

        return new Promise((resolve, reject) => {
            this.connect((conn) => {
                conn.sftp(async (err, sftp) => {
                    if (err) {
                        Print.error(`sftp下载文件报错`);
                        console.log(err);
                        reject(err);
                    }

                    const downFile = (p1, p2) => {
                        return new Promise((resolve, reject)=>{
                            sftp.fastGet(p1, p2, (err) => {
                                if (err) {
                                    Print.error(`sftp下载文件报错`);
                                    console.log(err);
                                    reject(err);
                                }
                                resolve(true);
                            });
                        });
                    }

                    for(let i = 0 ; i < files.length ; i++){
                        const file = files[i];
                        const remoteFilePath = file.path;
                        const localFilePath = path.join(localPath, file.relativePath);
                        Print.indigo(`正在下载: ${file.filename} size:${file.size}，已下载(${i}/${files.length})，${(i/files.length * 100).toFixed(0)}%`);
                        await downFile(remoteFilePath, localFilePath);
                    }
                    Print.success(`下载完成`);
                    conn.end();
                    resolve(true);
                });
            });
        });
    }

    /**
     * 扫描远程文件夹
     * @param {string} remotePath 远程文件夹路径
     * @returns Promise<ScanDirRes>
     */
    scanDir = (remotePath) => {
        return new Promise((resolve, reject) => {
            this.connect((conn) => {
                conn.sftp(async (err, sftp) => {
                    if (err) {
                        Print.error(`读取远程文件夹出错`);
                        console.log(err);
                        reject(err);
                    }
                    const result = {
                        dirs: [],
                        files: [],
                        size: 0,
                        filesNumber: 0,
                        dirsNumber: 0,
                    };
                    const readdir = (p) => {
                        return new Promise((resolve, reject) => {
                            sftp.readdir(p, async (err, list) => {
                                if (err) {
                                    Print.error(`读取远程文件夹出错`);
                                    console.log(err);
                                    reject(err);
                                }
                                for (let i = 0; i < list.length; i++) {
                                    const item = list[i];
                                    // console.log(item);
                                    const isDirectory = item.attrs.isDirectory();
                                    const isFile = item.attrs.isFile();
                                    // 文件处理
                                    if (isFile) {
                                        const filename = item.filename;
                                        const size = item.attrs.size;
                                        const path1 = `${p}/${filename}`;
                                        const dirPath = p;
                                        result.files.push({ path: path1, size, filename, dirPath });
                                        // 文件数量 +1
                                        result.filesNumber++;
                                        // 累计文件大小
                                        result.size += size;
                                    }
                                    // 文件夹处理
                                    if (isDirectory) {
                                        const newPath = `${p}/${item.filename}`;
                                        // 文件夹数量 +1
                                        result.dirsNumber++;
                                        result.dirs.push({ path: newPath });
                                        await readdir(newPath);
                                    }
                                }
                                resolve();
                            });
                        });
                    }
                    await readdir(remotePath);
                    conn.end();

                    result.dirs.forEach((dir) => {
                        const reg = new RegExp(`^${remotePath}`);
                        const relativePath = dir.path.replace(reg, '');
                        dir.relativePath = relativePath;
                    });

                    result.files.forEach((file) => {
                        const reg = new RegExp(`^${remotePath}`);
                        const relativePath = file.path.replace(reg, '');
                        file.relativePath = relativePath;
                    });
                    resolve(result)
                });
            });
        });
    }
}


module.exports = {
    MySSH,
};